/* eslint-disable react-hooks/exhaustive-deps */
'use client'

import { useEffect, useState } from 'react'
import { Message, UseChatHelpers, useChat } from 'ai/react'
import { useContext, createContext, useContextSelector } from 'use-context-selector'

import { useAppContextSelector } from './app-context'

import { useAvatar } from '@/hooks/use-avatar'
import { useSpeech } from '@/hooks/use-speech'
import { useCommander } from '@/hooks/use-commander'
import { extractJsonCommands, extractJsonContent } from '@/lib/chat-extractor'

type ChatContextValue = UseChatHelpers & {
    provider: 'openai' | 'azure'
    setProvider: (provider: 'openai' | 'azure') => void

    lastMessage: string

    sendMessage: (content: string) => void
    clearMessages: () => void,
}

const defaultValues: Partial<ChatContextValue> = {
    provider: 'azure',
    setProvider: () => { },

    lastMessage: '',

    sendMessage: () => { },
    clearMessages: () => { },
}
const ChatContext = createContext<ChatContextValue>(defaultValues as ChatContextValue)

type ChatContextProviderProps = {
    children: React.ReactNode
}

var trimmedText: string
var messageCache: string

export const ChatContextProvider = ({ children }: ChatContextProviderProps) => {
    const avatar = useAvatar(state => state.avatar)
    const messageWith = useAvatar(state => state.messagesWith)
    const updateMessages = useAvatar(state => state.updateMessages)

    const addCommands = useCommander(state => state.addCommands)

    const [lastMessage, setLastMessage] = useState<string>('')
    const [provider, setProvider] = useState<'openai' | 'azure'>('azure')

    const [currentApp, currentChatSession] = useAppContextSelector(state => [state.currentApp, state.currentChatSession])

    const {
        messages,
        error,
        append,
        reload,
        stop,
        setMessages,
        input,
        setInput,
        handleInputChange,
        handleSubmit,
        metadata,
        isLoading,
        data
    } = useChat({
        api: `/api/${avatar.provider ?? provider}/${avatar.useTools ? 'chat-with-tools' : 'chat'}`,
        onFinish: (message: Message) => {
            // console.log('chat-content.onFinished ----> message: ', message)

            const result = extractJsonCommands(message.content)
            // console.log('commands: ', result)
            if (result.length > 0) {
                addCommands(result)
            }
            setLastMessage(parseContent(message.content))
            messageCache = ''
            trimmedText = ''
        }
    })

    const [initSpeech, speak, speaker] = useSpeech(state => [state.init, state.speak, state.speaker])

    useEffect(() => {
        let messageBuf = messageWith(avatar.id)
        if (!messageBuf || messageBuf.length < 1) {
            messageBuf = [systemMessage()]
        }
        setMessages(messageBuf)
        messageCache = ''
        trimmedText = ''

        initSpeech()
    }, [avatar])

    const parseContent = (text: string) => {
        return avatar.useTools ? extractJsonContent(text, 'Teacher_talk') : text
    }

    useEffect(() => {
        if (messages.length > 0 && messages[messages.length - 1].role === 'assistant') {
            if (isLoading) {
                const currentMessage = messages[messages.length - 1]
                if (!messageCache || messageCache.length < 1) {
                    messageCache = ''
                    trimmedText = ''
                }
                const content = parseContent(currentMessage.content)
                trimmedText += content.substring(messageCache.length)
                messageCache = content

                let idx = Math.max(trimmedText.lastIndexOf('.'), trimmedText.lastIndexOf('!'), trimmedText.lastIndexOf('?'), trimmedText.lastIndexOf('\n'))
                if (idx != -1) {
                    let textReady = trimmedText.substring(0, idx + 1) + ' '
                    if (textReady && textReady.length) {
                        trimmedText = trimmedText.substring(idx + 1).trimStart()
                        speaker && speak(textReady, avatar.voice)
                    }
                }
            }
        }
        updateMessages(avatar.id, messages)
    }, [messages, isLoading])

    const sendMessage = async (content: string) => {
        append({
            id: Date.now().toString(),
            createdAt: new Date(),
            content: content,
            role: 'user'
        })
    }

    const clearMessages = () => setMessages([systemMessage()])
    const systemMessage = (): Message => ({ id: '0', role: 'system', createdAt: new Date(), content: avatar.prompt })

    return <ChatContext.Provider value={{
        provider,
        setProvider,

        lastMessage,

        sendMessage,
        clearMessages,

        messages,
        error,
        append,
        reload,
        stop,
        setMessages,
        input,
        setInput,
        handleInputChange,
        handleSubmit,
        metadata,
        isLoading,
        data
    }}>{children}</ChatContext.Provider>
}

export const useChatContext = () => {
    const context = useContext(ChatContext)
    if (!context) {
        throw new Error('use ChatContext inside ChatContextProvider to access the context value')
    }
    return context
}

export const useChatContextSelector: <T>(selector: (value: ChatContextValue) => T) => T = (selector) => {
    return useContextSelector(ChatContext, selector)
}